﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Web;
using FlightSearchServer.TicketSellingService;

namespace FlightSearchServer
{
    [ServiceContract]
    public interface IFlightSearchService
    {
        [OperationContract]
        [WebGet(UriTemplate = "flights?from={from}&to={to}&date={date}")]
        List<FlightInfo> Search(string from, string to, string date);

        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "sellers/{seller}/reservations")] 
        string Reserve(string seller, ReservationInfo reservationInfo);

        [OperationContract]
        [WebInvoke(Method = "DELETE", UriTemplate = "sellers/{seller}/reservations/{reservationId}")]
        void Cancel(string seller, string reservationId);
    }

    public class FlightSearchService : IFlightSearchService
    {
        public List<FlightInfo> Search(string from, string to, string date)
        {
            if (WebOperationContext.Current != null)
            {
                var flights = new List<FlightInfo>();

                foreach (var kvp in TicketSellersDatabase.Sellers)
                {
                    var seller = kvp.Key;
                    var channel = kvp.Value;

                    try
                    {
                        var result = channel.GetAvailableFlights(from, to, date);
                        flights.AddRange(result.Select(f => new FlightInfo
                        {
                            Seller = seller,
                            From = from,
                            To = to,
                            Date = date,
                            FlightId = f.FlightId,
                            AvailableSeats = f.AvailableSeats,
                            Price = f.Price,
                        }));
                    }
                    catch (CommunicationException e)
                    {
                        Console.WriteLine("Warning: failed to query {0} ({1}): {2}", seller, ((IClientChannel) channel).RemoteAddress, e.Message);
                    }
                }

                WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.OK;
                return flights;
            }

            return null;
        }

        public string Reserve(string seller, ReservationInfo reservationInfo)
        {
            if (WebOperationContext.Current != null)
            {
                ITicketSellingService channel;
                if ((channel = TicketSellersDatabase.GetSellerChannel(seller)) == null)
                {
                    WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
                    WebOperationContext.Current.OutgoingResponse.StatusDescription = "unknown seller";
                    return null;
                }

                try
                {
                    try
                    {
                        string reservationId = channel.SellTicket(reservationInfo.FlightId, reservationInfo.Date);
                        WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Created;
                        return reservationId;
                    }
                    catch (FaultException fe)
                    {
                        Console.WriteLine("Warning: failed to reserve ticket on seller {0} ({1}): {2}", seller, ((IClientChannel) channel).RemoteAddress, fe.Message);
                        WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Forbidden;
                        WebOperationContext.Current.OutgoingResponse.StatusDescription = fe.Message;
                    }
                }
                catch (CommunicationException e)
                {
                    Console.WriteLine("Warning: failed to query {0} ({1}): {2}", seller, ((IClientChannel) channel).RemoteAddress, e.Message);
                    WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.ServiceUnavailable;
                    WebOperationContext.Current.OutgoingResponse.StatusDescription = string.Format("{0} server is down", seller);
                }
            }

            return null;
        }

        public void Cancel(string seller, string reservationId)
        {
            if (WebOperationContext.Current != null)
            {
                ITicketSellingService channel;
                if ((channel = TicketSellersDatabase.GetSellerChannel(seller)) == null)
                {
                    WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
                    WebOperationContext.Current.OutgoingResponse.StatusDescription = "unknown seller";
                    return;
                }

                try
                {
                    try
                    {
                        channel.CancelTicket(reservationId);
                        WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.OK;
                    }
                    catch (FaultException fe)
                    {
                        Console.WriteLine("Warning: failed to cancel reservation on seller {0} ({1}): {2}", seller, ((IClientChannel) channel).RemoteAddress, fe.Message);
                        WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Forbidden;
                        WebOperationContext.Current.OutgoingResponse.StatusDescription = fe.Message;
                    }
                }
                catch (CommunicationException e)
                {
                    Console.WriteLine("Warning: failed to query {0} ({1}): {2}", seller, ((IClientChannel) channel).RemoteAddress, e.Message);
                    WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.ServiceUnavailable;
                    WebOperationContext.Current.OutgoingResponse.StatusDescription = string.Format("{0} server is down", seller);
                }
            }
        }
    }
}
